<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>对象的扩展运算符</title>
</head>
<body>
  <script>
    //解构 赋值
    // 对象的解构赋值用于从一个个对象取值，相当于将目标对象自身的所有可遍历的、但尚未被读取的属性，分配到指定的对象上面。
    // 所有的键和它们的值，都会被拷贝到新对象上面
    var {x,y,...z} = {x: 1, y: 2, a: 3, b: 4};
    console.log(x) // 1
    console.log(y) // 2
    console.log(z) // {a: 3, b: 4}
    /**
     * 上面代码中，变量z是解构赋值所在的对象，它获取等号右边的所有尚未读取的键(a和b),将它们连同值一起拷贝过来。
     * 由于解构赋值要求等号右边是一个对象，所以如果等号右边是undefined或null，就会报错，因为它们无法转为对象。
     *
     * 注意，解构赋值的拷贝是浅拷贝，即一个键的值是复合类型的值(数组，对象，函数)、那么解构赋值拷贝的是这个值的引用
     * 而不是这个值的副本。
     * 另外，扩展运算符的解构赋值，不能复制继承自原型对象的属性。
    */
  </script>
  <script>
    // 如果扩展运算符后面不是对象，则会自动将其转为对象。
    var a = {...1};
    console.log(a) // {}
    //等同于 { ...Object(1)}
    // 上面代码中，扩展运算符后面是整数1，会自动转为数值的包装对象Number{1}
    // 由于该对象没有自身属性，所以返回一个空对象。
    // 下面例子都是类似的道理
    // 等同于 {...Object(true)}
    var b = {...true} // {}

    // 等同于 {...Object(undefined)
    var c = {...undefined} // {}

    // 等同于 {...Object(null)}
    var d = {...null} // {}

    //但是，如果扩展运算符后面是字符串，它会自动转成一个类似数组的对象，因此返回的不是空对象。
    var e = {...'hello'}
    // {0: "h", 1: "e", 2: "l", 3: "l", 4: "o"}
    // 对象的扩展运算符等同于使用 Object.assign()方法。
    var a = {x: 1, y: 2}
    var aClone = { ...a }
    var cloneA = Object.assign({}, a);
    console.log(aClone) // {x: 1, y: 2}
    console.log(cloneA) // {x: 1, y: 2}
    //上面的例子只是拷贝了对象实例的属性，如果想完整克隆一个对象，还拷贝对象原型的属性，可以采用下面的写法。
    // 写法一
    var obj = {x: 1, y: 5}
    const clone1 = {
      __proto__: Object.getPrototypeOf(obj),
      ...obj
    };
    //写法二
    const clone2 = Object.assign(Object.create(Object.getPrototypeOf(obj)), obj);
    // 写法三
    const clone3 = Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptor(obj)) //getOwnPropertyDescriptor返回其属性描述符对象（property descriptor）
    // 上面代码中，写法一的__proto__属性在非浏览器的环境不一定部署，因此推荐使用写法二和写法三。
    // 扩展运算符可以用用于合并俩个对象
    var a = {x: 2, y: 5};
    var b = {x: 1, z: 6};
    var ab = {...a, ...b};
    //等同于
    var AB = Object.assign({}, a, b);
    console.log(ab) // {x: 1, y: 5, z: 6}
    console.log(AB) // {x: 1, y: 5, z: 6}
    var ba = {...b,...a}
    var BA = Object.assign({}, b, a)
    console.log(ba) // {x: 2, z: 6, y: 5}
    console.log(BA) // {x: 2, z: 6, y: 5}
    // 跟顺序有关，后面放的值会覆盖前面放的值。
    // 扩展运算符参数对象之中，如果有取值函数get，这个函数是会执行的
    var a = {
      y: 4,
      get x() {
        return 5
      }
    }
    var b = {...a}
    console.log(b) // {y: 4, x: 5}
  </script>
</body>
</html>